package graph;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.Vector;
import java.util.Map.Entry;

import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.DatabaseStats;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.StatsConfig;

public class DbEdgesImpl implements EdgesImpl {

	public DbEdgesImpl(String dirPath, String name) {
		try {
			// Open the environment. Create it if it does not already exist.
			EnvironmentConfig envConfig = new EnvironmentConfig();
			// envConfig.setCacheSize(56 * 1024 * 1024);
			envConfig.setAllowCreate(true);
			m_env = new Environment(new File(dirPath), envConfig);

			// Open the database. Create it if it does not already exist.
			DatabaseConfig dbConfig = new DatabaseConfig();
			dbConfig.setAllowCreate(true);
			m_db = m_env.openDatabase(null, name, dbConfig);
		} catch (DatabaseException dbe) {
			// Exception handling goes here
			dbe.printStackTrace();
		}
	}

	public void load(File file) {
		try {
			BufferedReader reader = new BufferedReader(new FileReader(file));
			while (true) {
				String line1 = reader.readLine();
				if (line1 == null) {
					break;
				}
				String line2 = reader.readLine();
				if (line2 == null) {
					break;
				}
				int id1 = Integer.parseInt(line1);
				String tokens[] = line2.split(";");
				int edgeNum = tokens.length / 2;
				IdWeight[] idWeights = new IdWeight[edgeNum];
				for (int i = 0; i < edgeNum; i++) {
					int id2 = Integer.parseInt(tokens[2 * i]);
					int count = Integer.parseInt(tokens[2 * i + 1]);
					idWeights[i] = new IdWeight(id2, count);
				}
				DatabaseEntry keyEntry = getKeyEntry(id1);
				DatabaseEntry valueEntry = getValueEntry(idWeights);
				m_db.put(null, keyEntry, valueEntry);
			}
			m_env.sync();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (DatabaseException e) {
			e.printStackTrace();
		}
	}

	private DatabaseEntry getKeyEntry(int key) {
		ByteBuffer buffer = ByteBuffer.allocate(4);
		buffer.putInt(key);
		DatabaseEntry entry = new DatabaseEntry(buffer.array());
		return entry;
	}

	private DatabaseEntry getValueEntry(IdWeight[] idWeights) {
		int num = idWeights.length;
		ByteBuffer buffer = ByteBuffer.allocate(8 * num);
		for (int i = 0; i < num; i++) {
			IdWeight idWeight = idWeights[i];
			buffer.putInt(idWeight.m_id);
			buffer.putInt((int) idWeight.m_weight);
		}
		return new DatabaseEntry(buffer.array());
	}

	//
	// Vector<IdWeight> getIdWeights(Set<Edge> edges, NodeType fromType) {
	// Vector<IdWeight> idWeightVec = new Vector<IdWeight>();
	// Iterator<Edge> edgeIt = edges.iterator();
	// while (edgeIt.hasNext()) {
	// Edge edge = edgeIt.next();
	// IdWeight idWeight = null;
	// if (fromType == NodeType.SRC) {
	// idWeight = new IdWeight(edge.m_desId, (int) edge.m_weight);
	// } else {
	// idWeight = new IdWeight(edge.m_srcId, (int) edge.m_weight);
	// }
	// idWeightVec.add(idWeight);
	// }
	// return idWeightVec;
	// }

	Database m_db;
	Environment m_env;

	public void add(int nodeId, IdWeight[] idWeights) {
		IdWeight[] existIdWeights = getEdgesFrom(nodeId);
		Map<Integer, Double> idWeightMap = createIdWeightMap(existIdWeights);
		int recordNum = idWeights.length;
		for (int i = 0; i < recordNum; i++) {
			idWeightMap.put(idWeights[i].m_id, idWeights[i].m_weight);
		}
		IdWeight[] allIdWeights = createIdWeightArray(idWeightMap);
		DatabaseEntry key1 = getKeyEntry(nodeId);
		DatabaseEntry value1 = getValueEntry(allIdWeights);
		try {
			m_db.put(null, key1, value1);
		} catch (DatabaseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private IdWeight[] createIdWeightArray(Map<Integer, Double> idWeightMap) {
		IdWeight[] weights = new IdWeight[idWeightMap.size()];
		Iterator<Entry<Integer, Double>> entryIt = idWeightMap.entrySet()
				.iterator();
		int index = 0;
		while (entryIt.hasNext()) {
			Entry<Integer, Double> entry = entryIt.next();
			IdWeight idWeight = new IdWeight(entry.getKey(), entry.getValue());
			weights[index] = idWeight;
		}
		return weights;
	}

	private Map<Integer, Double> createIdWeightMap(IdWeight[] existIdWeights) {
		Map<Integer, Double> idWeightMap = new HashMap<Integer, Double>();
		int recordNum = existIdWeights.length;
		for (int i = 0; i < recordNum; i++) {
			idWeightMap.put(existIdWeights[i].m_id, existIdWeights[i].m_weight);
		}
		return idWeightMap;
	}

	public IdWeight[] getEdgesFrom(int nodeId) {
		IdWeight[] idWeights = null;
		DatabaseEntry keyEntry = getKeyEntry(nodeId);
		DatabaseEntry valueEntry = new DatabaseEntry();
		try {
			m_db.get(null, keyEntry, valueEntry, LockMode.DEFAULT);
			byte[] data = valueEntry.getData();
			if (data == null) {
				return null;
			}
			Vector<IdWeight> idWeightVec = new Vector<IdWeight>();
			ByteBuffer buffer = ByteBuffer.wrap(data);
			while (true) {
				int id2 = buffer.getInt();
				int weight = buffer.getInt();
				IdWeight idWeight = new IdWeight(id2, weight);
				idWeightVec.add(idWeight);
				if (buffer.position() >= buffer.limit()) {
					break;
				}
			}
			idWeights = idWeightVec.toArray(new IdWeight[0]);
		} catch (DatabaseException e) {
			e.printStackTrace();
		}
		return idWeights;
	}

	public Set<Integer> getDesNodes() {
		Set<Integer> desNodes = new TreeSet<Integer>();
		try {
			Cursor cursor = m_db.openCursor(null, null);
			DatabaseEntry key = new DatabaseEntry();
			DatabaseEntry data = new DatabaseEntry();
			cursor.getFirst(key, data, LockMode.DEFAULT);
			byte[] dataBytes = data.getData();
			if (dataBytes == null) {
				return null;
			}
			ByteBuffer buffer = ByteBuffer.wrap(dataBytes);
			while (true) {
				while (true) {
					int id2 = buffer.getInt();
					buffer.getInt();
					desNodes.add(id2);
					if (buffer.position() >= buffer.limit()) {
						break;
					}
				}
				OperationStatus status = cursor.getNext(key, data, LockMode.DEFAULT);
				if(status != OperationStatus.SUCCESS){
					break;
				}
				dataBytes = data.getData();
				if (dataBytes == null) {
					return null;
				}
				buffer = ByteBuffer.wrap(dataBytes);
			}
		} catch (DatabaseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return desNodes;
	}

	public Set<Integer> getSrcNodes() {
		// TODO �Զ���ɷ������
		return null;
	}
}
